#include "pickmanager.h"
#include "scene.h"
#include "viewer.h"
#include <osgUtil\PolytopeIntersector>
#include "actorbase.h"
#include <osgUtil\IntersectVisitor>
#include "layersmanager.h"

namespace VrDataCore
{
	PickManager* PickManager::_manager = NULL;

	PickManager& PickManager::GetInstance()
	{
		if(_manager == NULL)
		{
			_manager = new PickManager;
		}
		return *_manager;
	}

	PickManager::PickManager():_currentScene(NULL),_currentView(NULL)
	{
		_lineRectShape = new CoreBase::LineRectShape;
		_traversalMask = NODE_MASK_NOMAL;
	}

	PickManager::~PickManager()
	{}

	void PickManager::initPickManager(CoreBase::Viewer* view,CoreBase::Scene* scene)
	{
		_currentScene = scene;
		_currentView = view;
	}

	void PickManager::changeScene(CoreBase::Scene* scene)
	{
		_currentScene = scene;
	}

	void PickManager::clearAllPickObjects()
	{
		QMap<CoreBase::ActorBase*,osg::ref_ptr<SelectEffect>>::Iterator iter = _boundingBoxEffectMap.begin();
		for(;iter!=_boundingBoxEffectMap.end();++iter)
		{
			iter.value()->unApply(iter.key());
		}
		_boundingBoxEffectMap.clear();
		_drawableVector.clear();
		_intersectionVector.clear();
	}

	void PickManager::addPickObjects(QVector<CoreBase::ActorBase*> objectvector)
	{
		clearAllPickObjects();
		for(int i=0;i<objectvector.size();++i)
		{
			_drawableVector.push_back(objectvector.at(i)->getDrawable());
		}
		showSelectActors();
		callRegisterFun();
	}

	void PickManager::upDateSelectRect(int w, int l)
	{
		_lineRectShape->updateSize(_rectBase,w,l);
	}

	void PickManager::upDateSelectRect(const osg::Vec3& newpoint)
	{
		int w,l;
		w = newpoint.x() - _rectBase.x();
		l = newpoint.y() - _rectBase.y();
		upDateSelectRect(w,l);
	}

	void PickManager::setSelectRectBase(const osg::Vec3& base)
	{
		_rectBase = base;
	}

	void PickManager::pickObjects(const osg::Vec2 &mousepos,bool addpick,unsigned int traversalmask,const QString& classname
		,QVector<CoreBase::Drawable*> selectenabledrawables)
	{
		_intersectionVector.clear();
		if(_currentScene == NULL || _currentView == NULL)
			return;
		if(!addpick)
			clearAllPickObjects();
		osgUtil::LineSegmentIntersector::Intersections hitlist;
		dynamic_cast<osgViewer::View*>(_currentView)->computeIntersections(mousepos.x(), mousepos.y(),
			hitlist, traversalmask);
		QVector<CoreBase::Drawable*> drawables = _currentScene->getAllDrawablesInTheScene();
		if(selectenabledrawables.size() == 0)
		{
			for(int i=0;i<drawables.size();++i)
			{
				if(drawables.at(i)->getOwnerActor()!=NULL && drawables.at(i)->getOwnerActor()->isInstanceOf(classname)
					&& !drawables.at(i)->getOwnerActor()->isInstanceOf(CLASS_LAYER))
					selectenabledrawables.push_back(drawables.at(i));
			}
		}
		QVector<CoreBase::Drawable*> nowdrawables;
		bool findsuccess = false;
		for (std::multiset<osgUtil::LineSegmentIntersector::Intersection>::const_iterator hitItr = hitlist.begin();
			hitItr != hitlist.end();
			++hitItr)
		{
			for (osg::NodePath::const_reverse_iterator nodeItr = hitItr->nodePath.rbegin();
				nodeItr != hitItr->nodePath.rend();
				++nodeItr)
			{
				for (QVector<CoreBase::Drawable*>::iterator drawableItr = selectenabledrawables.begin();
					drawableItr != selectenabledrawables.end();
					++drawableItr)
				{
					if ((*nodeItr) == (*drawableItr)->getOSGNode()  && !nowdrawables.contains(*drawableItr))
					{
						nowdrawables.push_back(*drawableItr);
						_intersectionVector.push_back(*hitItr);
						findsuccess = true;
						break;
					}
				}
				if(findsuccess)
					break;
			}
			if(findsuccess)
				break;
		}
		if(addpick)
		{
			for(int i=0;i<nowdrawables.size();++i)
			{
				int index = _drawableVector.indexOf(nowdrawables.at(i));
				if(index!=-1)
				{
					CoreBase::ActorBase* removeactor = nowdrawables.at(i)->getOwnerActor();
					_boundingBoxEffectMap.value(removeactor)->unApply(removeactor);
					_boundingBoxEffectMap.remove(removeactor);
					_drawableVector.remove(index);
				}
				else
					_drawableVector.push_back(nowdrawables.at(i));
			}
		}
		else
			_drawableVector = nowdrawables;
		showSelectActors();
		callRegisterFun();
	}

	QVector<CoreBase::ActorBase*> PickManager::pickObjectsNoUpdate(const osg::Vec2 &mousepos,unsigned int traversalmask,
		const QString& classname,QVector<CoreBase::Drawable*> selectenabledrawables)
	{
		_intersectionVector.clear();
		QVector<CoreBase::ActorBase*> returnvalue;
		if(_currentScene == NULL || _currentView == NULL)
			return returnvalue;
		osgUtil::LineSegmentIntersector::Intersections hitlist;
		dynamic_cast<osgViewer::View*>(_currentView)->computeIntersections(mousepos.x(), mousepos.y(),
			hitlist, traversalmask);
		QVector<CoreBase::Drawable*> drawables = _currentScene->getAllDrawablesInTheScene();
		if(selectenabledrawables.size() == 0)
		{
			for(int i=0;i<drawables.size();++i)
			{
				if(drawables.at(i)->getOwnerActor()!=NULL && drawables.at(i)->getOwnerActor()->isInstanceOf(classname)
					&& !drawables.at(i)->getOwnerActor()->isInstanceOf(CLASS_LAYER))
					selectenabledrawables.push_back(drawables.at(i));
			}
		}
		bool findsuccess = false;
		for (std::multiset<osgUtil::LineSegmentIntersector::Intersection>::const_iterator hitItr = hitlist.begin();
			hitItr != hitlist.end();
			++hitItr)
		{
			for (osg::NodePath::const_reverse_iterator nodeItr = hitItr->nodePath.rbegin();
				nodeItr != hitItr->nodePath.rend();
				++nodeItr)
			{
				for (QVector<CoreBase::Drawable*>::iterator drawableItr = selectenabledrawables.begin();
					drawableItr != selectenabledrawables.end();
					++drawableItr)
				{
					if ((*nodeItr) == (*drawableItr)->getOSGNode()  && !returnvalue.contains((*drawableItr)->getOwnerActor()))
					{
						returnvalue.push_back((*drawableItr)->getOwnerActor());
						_intersectionVector.push_back(*hitItr);
						findsuccess = true;
						break;
					}
				}
				if(findsuccess)
					break;
			}
			if(findsuccess)
				break;
		}
		return returnvalue;
	}

	QVector<osgUtil::LineSegmentIntersector::Intersection> PickManager::getPreviousLineSegmentIntersectorIntersection()
	{
		return _intersectionVector;
	}

	void PickManager::showSelectActors()
	{
		for(int i=0;i<_drawableVector.size();++i)
		{
			CoreBase::ActorBase* actor = _drawableVector.at(i)->getOwnerActor();
			if(!_boundingBoxEffectMap.contains(actor))
			{
				osg::ref_ptr<SelectEffect> boundingbox = new SelectEffect;
				_boundingBoxEffectMap.insert(actor,boundingbox);
				boundingbox->apply(actor);
			}
		}
	}

	void PickManager::pickObjects(const osg::Vec2 &mouseend,bool andbase,bool addpick,unsigned int traversalmask,
		const QString& classname,QVector<CoreBase::Drawable*> selectenabledrawables)
	{
		if(andbase)
			pickObjects(osg::Vec2(_rectBase.x(),_rectBase.y()),mouseend,traversalmask,classname,addpick,selectenabledrawables);
		else
			pickObjects(mouseend,addpick,traversalmask,classname,selectenabledrawables);
	}

	void PickManager::pickObjects(const osg::Vec2 &mousestart,const osg::Vec2 &mouseend,unsigned int traversalmask
		,const QString& classname,bool addpick,QVector<CoreBase::Drawable*> selectenabledrawables)
	{
		_intersectionVector.clear();
		if(_currentScene == NULL || _currentView == NULL)
			return;
		if(!addpick)
			clearAllPickObjects();
		float minx,miny,maxx,maxy;
		minx = mousestart.x()>mouseend.x()?mouseend.x():mousestart.x();
		miny = mousestart.y()>mouseend.y()?mouseend.y():mousestart.y();
		maxx = mousestart.x()>mouseend.x()?mousestart.x():mouseend.x();
		maxy = mousestart.y()>mouseend.y()?mousestart.y():mouseend.y();
		osg::ref_ptr<osgUtil::PolytopeIntersector> picker = new osgUtil::PolytopeIntersector( 
			osgUtil::Intersector::WINDOW, minx, miny, maxx, maxy );
		//picker->setDimensionMask(traversalmask);
		osgUtil::IntersectionVisitor iv(picker);
		iv.setTraversalMask(traversalmask);
		_currentView->getCamera()->accept(iv);
		QVector<CoreBase::Drawable*> drawables = _currentScene->getAllDrawablesInTheScene();
		if(selectenabledrawables.size() == 0)
		{
			for(int i=0;i<drawables.size();++i)
			{
				if(drawables.at(i)->getOwnerActor()!=NULL && drawables.at(i)->getOwnerActor()->isInstanceOf(classname)
					&& !drawables.at(i)->getOwnerActor()->isInstanceOf(CLASS_LAYER))
					selectenabledrawables.push_back(drawables.at(i));
			}
		}
		QVector<CoreBase::Drawable*> nowdrawables;
		if (picker->containsIntersections())
		{
			osgUtil::PolytopeIntersector::Intersections intersections;
			osgUtil::PolytopeIntersector::Intersections::iterator iter;
			intersections = picker->getIntersections();
			for (iter = intersections.begin();iter!= intersections.end();iter++)
			{
				osg::NodePath nodePath = (*iter).nodePath;
				for (osg::NodePath::const_reverse_iterator nodeItr = nodePath.rbegin();
					nodeItr != nodePath.rend();
					++nodeItr)
				{
					for (QVector<CoreBase::Drawable*>::iterator drawableItr = selectenabledrawables.begin();
						drawableItr != selectenabledrawables.end();
						++drawableItr)
					{
						if ((*nodeItr) == (*drawableItr)->getOSGNode() && !nowdrawables.contains(*drawableItr))
						{
							nowdrawables.push_back(*drawableItr);
						}
					}
				}
			}
		}
		if(addpick)
		{
			for(int i=0;i<nowdrawables.size();++i)
			{
				int index = _drawableVector.indexOf(nowdrawables.at(i));
				if(index!=-1)
				{
					CoreBase::ActorBase* removeactor = nowdrawables.at(i)->getOwnerActor();
					_boundingBoxEffectMap.value(removeactor)->unApply(removeactor);
					_boundingBoxEffectMap.remove(removeactor);
					_drawableVector.remove(index);
				}
				else
					_drawableVector.push_back(nowdrawables.at(i));
			}
		}
		else
			_drawableVector = nowdrawables;
		showSelectActors();
		callRegisterFun();
	}

	QVector<CoreBase::Drawable*> PickManager::getPickObjects()
	{
		if(_currentScene == NULL || _currentView == NULL)
		{
			QVector<CoreBase::Drawable*> returnvec;
			return returnvec;
		}
		return _drawableVector;
	}

	QVector<CoreBase::ActorBase*> PickManager::getPickActorObjects()
	{
		QVector<CoreBase::ActorBase*> returnvalue;
		for(int i=0;i<_drawableVector.size();++i)
		{
			returnvalue.push_back(_drawableVector.at(i)->getOwnerActor());
		}
		return returnvalue;
	}

	void PickManager::callRegisterFun()
	{
		QMap<QString,osg::ref_ptr<FuncBase>>::Iterator iter = _funcMap.begin();
		for(;iter!=_funcMap.end();++iter)
		{
			iter.value()->callFunc();
		}
	}

	CoreBase::Drawable* PickManager::getSelectionRectNode()
	{
		return _lineRectShape;
	}

	void PickManager::viewResize(const int w,const int h)
	{
		_lineRectShape->viewResize(w,h);
	}

	void PickManager::getLineSegmentIntersectResult(const osg::Vec3& start,const osg::Vec3& end,osg::Vec3& point,
		osg::Vec3& normal,unsigned int traversalmask)
	{
		osgUtil::IntersectVisitor iv; 
		iv.setTraversalMask(traversalmask); 
		osg::ref_ptr<osg::LineSegment> segUp = new osg::LineSegment; 
		segUp->set(start,end); 
		iv.addLineSegment(segUp.get()); 
		VrDataCore::LayersManager::GetInstance().getNoEnvironmentLayer()->getDrawable()->getOSGNode()->accept(iv);
		if (iv.hits()) 
		{ 
			osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(segUp.get()); 
			if (!hitList.empty()) 
			{ 
				point = hitList.front().getWorldIntersectPoint(); 
				normal = hitList.front().getWorldIntersectNormal(); 
			}
		}
	}

	void PickManager::getLineSegmentIntersectResultInCollisionLayer(const osg::Vec3& start,const osg::Vec3& end,osg::Vec3& point,osg::Vec3& normal)
	{
		osgUtil::IntersectVisitor iv; 
		iv.setTraversalMask(NODE_MASK_ALL); 
		osg::ref_ptr<osg::LineSegment> segUp = new osg::LineSegment; 
		segUp->set(start,end); 
		iv.addLineSegment(segUp.get()); 
		VrDataCore::LayersManager::GetInstance().getCollisionLayer()->getDrawable()->getOSGNode()->accept(iv);
		if (iv.hits()) 
		{ 
			osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(segUp.get()); 
			if (!hitList.empty()) 
			{ 
				point = hitList.front().getWorldIntersectPoint(); 
				normal = hitList.front().getWorldIntersectNormal(); 
			}
		}
	}
}